package com.example.mydemoproject.service.rollback;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.mydemoproject.config.sqlSession.SqlContext;
import com.example.mydemoproject.dao.RollBaclDemoMapper;
import com.example.mydemoproject.model.entity.RollBaclDemo;
import com.example.mydemoproject.util.ListUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@Service
@RequiredArgsConstructor
public class MultithreadingRollBackService extends ServiceImpl<RollBaclDemoMapper, RollBaclDemo> {

    private final SqlContext sqlContext;
    private final RollBaclDemoMapper rollBaclDemoMapper;

    @Resource(name = "globalThreadPoolExecutor")
    private final ExecutorService service;

    /**
     * 测试多线程事务.
     * @param employeeDOList
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveThreadHaveErr(List<RollBaclDemo> employeeDOList) {
        try {
            //先做删除操作,如果子线程出现异常,此操作不会回滚
            Integer integer = rollBaclDemoMapper.selectCount(null);
            LambdaQueryWrapper<RollBaclDemo> wrapper = new LambdaQueryWrapper();
            wrapper.last("limit " + integer);
            this.getBaseMapper().delete(wrapper);
            //拆分数据,拆分5份
            List<List<RollBaclDemo>> lists = ListUtils.averageAssign(employeeDOList, 5);
            log.info("lists的size:{}", lists.size());
            //执行的线程
            Thread []threadArray = new Thread[lists.size()];
            //监控子线程执行完毕,再执行主线程,要不然会导致主线程关闭,子线程也会随着关闭
            CountDownLatch countDownLatch = new CountDownLatch(lists.size());
            for (int i =0; i<lists.size(); i++){
                List<RollBaclDemo> list  = lists.get(i);
                int finalI = i;
                threadArray[i] =  new Thread(() -> {
                    try {
                        //最后一个线程抛出异常
                        log.info("分段集合:{}", JSON.toJSONString(list));
                        if (finalI == lists.size()-1){
                            throw new RuntimeException("001 出现异常");
                        }
                        //批量添加,mybatisPlus中自带的batch方法
//                        this.saveBatch(list);
                        for (RollBaclDemo rollBaclDemo : list) {
                            log.info("插入数据:{}", JSON.toJSONString(rollBaclDemo));
                            rollBaclDemoMapper.insert(rollBaclDemo);
                        }
                    }finally {
                        countDownLatch.countDown();
                    }
                });
            }
            for (int i = 0; i <lists.size(); i++){
                service.execute(threadArray[i]);
            }
            //当子线程执行完毕时,主线程再往下执行
            countDownLatch.await();
            System.out.println("添加完毕");
        }catch (Exception e){
            log.info("error",e);
            throw new RuntimeException("002 出现异常");
        }
    }


    @SneakyThrows
    public void saveThread(List<RollBaclDemo> employeeDOList) {
        // 获取数据库连接,获取会话(内部自有事务)
        SqlSession sqlSession = sqlContext.getSqlSession();
        Connection connection = sqlSession.getConnection();
        try {
            // 设置手动提交
            connection.setAutoCommit(false);
            RollBaclDemoMapper employeeMapper = sqlSession.getMapper(RollBaclDemoMapper.class);
            //先做删除操作
            Integer integer = employeeMapper.selectCount(null);
            LambdaQueryWrapper<RollBaclDemo> wrapper = new LambdaQueryWrapper();
            wrapper.last("limit " + integer);
            employeeMapper.delete(wrapper);
            List<Callable<Integer>> callableList  = new ArrayList<>();
            List<List<RollBaclDemo>> lists = ListUtils.averageAssign(employeeDOList, 5);
            for (int i =0;i<lists.size();i++){
                List<RollBaclDemo> list  = lists.get(i);
                Callable<Integer> callable = new Callable<Integer>() {
                    @Override
                    public Integer call() throws Exception {
//                        saveBatch(list);
                        try {
                            for (RollBaclDemo rollBaclDemo : list) {
                                employeeMapper.insert(rollBaclDemo);
                            }
                            int aa = 1/0;
                            return 1;
                        } catch (Exception e) {
                            log.error("", e);
                            return -1;
                        }

                    }
                };
                callableList.add(callable);
            }
            //执行子线程
            List<Future<Integer>> futures = service.invokeAll(callableList);
            for (Future<Integer> future:futures) {
                if (future.get()<=0){
                    connection.rollback();
                    return;
                }
            }
            connection.commit();
            System.out.println("添加完毕");
        }catch (Exception e){
            connection.rollback();
            log.info("error",e);
            throw new RuntimeException("002 出现异常");
        }
    }

}
